import { fakeAsync } from '@angular/core/testing';
import { SpectatorHost } from '@ngneat/spectator';

import { runLoader, setlistenToLangChange } from '../mocks';
import { TranslocoDirective } from '../../transloco.directive';
import { TranslocoService } from '../../transloco.service';

import {
  createFactory,
  testMergedScopedTranslation,
  testScopedTranslation,
  testTranslationWithPrefix,
} from './shared';

describe('Structural directive', () => {
  let spectator: SpectatorHost<TranslocoDirective>;
  const createHost = createFactory();

  it(`GIVEN structural directive with multiple keys
      WHEN translations are loaded
      THEN should display all translated values`, fakeAsync(() => {
    spectator = createHost(`
        <section *transloco="let t;">
           <div>{{t('home') }}</div>
           <span>{{t('fromList')}}</span>
           <p>{{t('a.b.c')}}</p>
           <h2>{{t('a.b.c', {fromList: "value"}) }}</h2>
        </section>
     `);
    runLoader();
    // fakeAsync doesn't trigger CD
    spectator.detectChanges();
    expect(spectator.queryHost('div')).toHaveText('home english');
    expect(spectator.queryHost('span')).toHaveText('from list');
    expect(spectator.queryHost('p')).toHaveText('a.b.c from list english');
    expect(spectator.queryHost('h2')).toHaveText('a.b.c value english');
  }));

  it(`GIVEN structural directive with reRenderOnLangChange enabled
      WHEN active language changes
      THEN should update all translations`, fakeAsync(() => {
    spectator = createHost(
      `
        <section *transloco="let t;">
           <div>{{t('home') }}</div>
           <span>{{t('fromList')}}</span>
           <p>{{t('a.b.c')}}</p>
           <h2>{{t('a.b.c', {fromList: "value"}) }}</h2>
        </section>
     `,
      { detectChanges: false },
    );
    const service = spectator.inject(TranslocoService);
    setlistenToLangChange(service);
    spectator.detectChanges();
    runLoader();
    // fakeAsync doesn't trigger CD
    spectator.detectChanges();
    expect(spectator.queryHost('div')).toHaveText('home english');
    expect(spectator.queryHost('span')).toHaveText('from list');
    expect(spectator.queryHost('p')).toHaveText('a.b.c from list english');
    expect(spectator.queryHost('h2')).toHaveText('a.b.c value english');
    (service as TranslocoService).setActiveLang('es');
    runLoader();
    spectator.detectChanges();
    expect(spectator.queryHost('div')).toHaveText('home spanish');
    expect(spectator.queryHost('span')).toHaveText('from list');
    expect(spectator.queryHost('p')).toHaveText('a.b.c from list spanish');
    expect(spectator.queryHost('h2')).toHaveText('a.b.c value spanish');
  }));

  it(`GIVEN directive with lang changes enabled
      WHEN language changes multiple times
      THEN should create embedded view only once`, fakeAsync(() => {
    spyOn(
      TranslocoDirective.prototype as any,
      'resolveLoadingContent',
    ).and.callThrough();
    spectator = createHost(`<section *transloco="let t"></section>`, {
      detectChanges: false,
    });
    const service = spectator.inject(TranslocoService);

    setlistenToLangChange(service);
    spectator.detectChanges();
    runLoader();
    service.setActiveLang('es');
    runLoader();
    expect(
      (TranslocoDirective.prototype as any).resolveLoadingContent,
    ).toHaveBeenCalledTimes(1);
  }));

  it(`GIVEN directive without reRenderOnLangChange mode
      WHEN active language changes
      THEN should not update translation`, fakeAsync(() => {
    spectator = createHost(`<div transloco="home"></div>`);
    runLoader();
    expect(spectator.queryHost('[transloco]')).toHaveText('home english');
    spectator.inject(TranslocoService).setActiveLang('es');
    spectator.detectChanges();
    runLoader();
    expect(spectator.queryHost('[transloco]')).toHaveText('home english');
  }));

  describe('Scope', () => {
    it(`GIVEN structural directive with scope
        WHEN scoped translations are loaded
        THEN should display scoped translation`, fakeAsync(() => {
      spectator = createHost(
        `<section *transloco="let t; scope: 'lazy-page'"><div>{{t('lazyPage.title')}}</div></section>`,
        {
          detectChanges: false,
        },
      );
      testScopedTranslation(spectator);
    }));

    it(`GIVEN directive with scope and global keys
        WHEN scoped translation loads before global
        THEN should load scoped translation correctly`, fakeAsync(() => {
      spectator = createHost(
        `
        <section *transloco="let t; scope: 'lazy-page'">
        <div class="scoped">{{t('lazyPage.title')}}</div>
        <div class="global">{{t('home')}}</div>
        </section>`,
        {
          detectChanges: false,
        },
      );
      testMergedScopedTranslation(spectator);
    }));

    it(`GIVEN directive with scope and global keys
        WHEN both translations are loaded
        THEN should expose both scoped and global translation`, fakeAsync(() => {
      spectator = createHost(
        `
        <section *transloco="let t; scope: 'lazy-page'">
        <div class="scoped">{{t('lazyPage.title')}}</div>
        <div class="global">{{t('home')}}</div>
        </section>`,
        {
          detectChanges: false,
        },
      );
      testMergedScopedTranslation(spectator, true);
    }));
  });

  describe('Prefix', () => {
    it(`GIVEN directive with prefix
        WHEN translations are loaded
        THEN should get translation of nested property`, fakeAsync(() => {
      spectator = createHost(
        `<section *transloco="let t; prefix: 'nested'"><div>{{t('title')}}</div></section>`,
        {
          detectChanges: false,
        },
      );
      testTranslationWithPrefix(spectator);
    }));
  });

  describe('CurrentLang', () => {
    beforeEach(fakeAsync(() => {
      spectator = createHost(
        `
        <section *transloco="let t; currentLang as currentLang">
           <div>{{ currentLang }}</div>
        </section>
     `,
        { detectChanges: false },
      );
      const service = spectator.inject(TranslocoService);
      setlistenToLangChange(service);
      spectator.detectChanges();
      runLoader();
    }));

    it(`GIVEN directive with currentLang variable
        WHEN translations are loaded
        THEN should expose currentLang to the template`, fakeAsync(() => {
      spectator.detectChanges();
      expect(spectator.queryHost('div')).toHaveText('en');
    }));

    it(`GIVEN directive with currentLang variable
        WHEN active language changes
        THEN should update currentLang value`, fakeAsync(() => {
      spectator.inject(TranslocoService).setActiveLang('es');
      runLoader();
      spectator.detectChanges();
      expect(spectator.queryHost('div')).toHaveText('es');
    }));
  });
});
